package com.tn.wservice;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXB;
import javax.xml.namespace.QName;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SAAJResult;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.dom.DOMSource;

import com.tn.service.dwr.jaxws.GetAutogeneratedOrderNumber;
import com.tn.service.dwr.jaxws.GetAutogeneratedOrderNumberResponse;
import com.tn.service.dwr.jaxws.Validate;
import com.tn.service.dwr.jaxws.ValidateResponse;
import com.tn.wservice.IYSNWebService.ParamKey;
import com.tn.xfer.DwrResponse;

public class SOAPHandler {

	public static final String NAMESPACE_URI = "http://dwr.service.tn.com/";
	private static final QName GET_ORDER_NUM = new QName(NAMESPACE_URI, "getAutogeneratedOrderNumber");
	private static final QName VALIDATE = new QName(NAMESPACE_URI, "validate");

	private HashMap<QName, IYSNWebService> serviceRegistry = new HashMap<QName, IYSNWebService>();
	private MessageFactory messageFactory;
	private POAdapter adapter;
	private static SOAPHandler instance = new SOAPHandler();

	private SOAPHandler() {
		try {
			messageFactory = MessageFactory.newInstance();
		} catch (SOAPException e) {
			e.printStackTrace();
		}
		adapter = new POAdapter();
	}

	public static SOAPHandler getInstance() {
		return instance;
	}

	@SuppressWarnings("unchecked")
	public SOAPMessage handleSOAPRequest(SOAPMessage request) {
		SOAPMessage soapResponse = null;
		SOAPBody responseSoapBody = null;
		try {
			soapResponse = messageFactory.createMessage();
			responseSoapBody = soapResponse.getSOAPBody();
			SOAPBody soapBody = request.getSOAPBody();

			Iterator<?> iterator = soapBody.getChildElements();
			Object responsePojo = null;
			while (iterator.hasNext()) {
				Object next = iterator.next();
				if (next instanceof SOAPElement) {
					SOAPElement soapElement = (SOAPElement) next;
					QName qname = soapElement.getElementQName();
					if (GET_ORDER_NUM.equals(qname)) {
						responsePojo = handleGetAutogeneratedOrderNumber(soapElement);
					}
					else if (VALIDATE.equals(qname)) {
						responsePojo = handleLogin(soapElement);
					}
					else {

						IYSNWebService serviceInstance = this.serviceRegistry
								.get(qname);

						// write a method to get all the request params from
						// request
						// into hashmap.
						// get all the attachments.

						Iterator<AttachmentPart> attachments = request
								.getAttachments();
						List<IYSNAttachment> attachmentList = new ArrayList<IYSNAttachment>();
						while (attachments.hasNext()) {
							AttachmentPart attachment = attachments.next();
							YSNAttachmentImpl impl = new YSNAttachmentImpl(
									attachment);
							attachmentList.add(impl);
						}

						String methodName = qname.getLocalPart();
						invokeService(serviceInstance, soapElement,
								attachmentList, methodName);
					}
				}
			}
			if (responsePojo != null) {
				JAXB.marshal(responsePojo, new SAAJResult(responseSoapBody));
			}
		} catch (Exception e) {
			try {
				SOAPFault fault = responseSoapBody.addFault();
				fault.setFaultString("Exception occured " + e.getMessage());
			} catch (Exception exp) {
				// do noting
			}
		} catch (Throwable e) {
			try {
				SOAPFault fault = responseSoapBody.addFault();
				fault.setFaultString("Fatal exception occured "
						+ e.getMessage());
			} catch (Exception exp) {
				// do noting
			}
		}
		return soapResponse;
	}

	private Object invokeService(IYSNWebService serviceInstance,
			SOAPElement soapElement, List<IYSNAttachment> attachmentList,
			String methodName) throws Exception {
		Class<? extends IYSNWebService> clazz = serviceInstance.getClass();

		Method[] methods = clazz.getMethods();
		Method serviceMethod = null;
		for (int i = 0; i < methods.length; i++) {
			if (!methodName.equals(methods[i].getName())) {
				continue;
			}

			serviceMethod = methods[i];
			break;
		}

		if (null == serviceMethod) {
			System.err.println("No valid method found.");
		}

		Class<?>[] parameterTypes = serviceMethod.getParameterTypes();

		if (parameterTypes.length != 1) {
			System.err.println("Invalid Method declaration.");
			throw new Exception("Invalid Method declaration :- " + methodName);
		}

		BusinessObject annotation = serviceMethod
				.getAnnotation(BusinessObject.class);
		Class<?> inputClass = annotation.value();

		Map<ParamKey, Object> methodParams = new HashMap<ParamKey, Object>();
			methodParams.put(ParamKey.BUSINESS_OBJECT, JAXB.unmarshal(
				new DOMSource(soapElement), inputClass));
		methodParams.put(ParamKey.ATTACHMENTS, attachmentList);
		methodParams.put(ParamKey.HEADERS, null);

		return serviceMethod.invoke(serviceInstance, methodParams);
	}

	private Object handleGetAutogeneratedOrderNumber(SOAPElement soapElement) {
		
		GetAutogeneratedOrderNumber req = JAXB.unmarshal(new DOMSource(
				soapElement), GetAutogeneratedOrderNumber.class);
		DwrResponse resp = adapter.getAutogeneratedOrderNumber(req.getArg0());
		GetAutogeneratedOrderNumberResponse ret = new GetAutogeneratedOrderNumberResponse();
		ret.setReturn(resp);
		return ret;
	}
	
	private Object handleLogin(SOAPElement soapElement) {
		
		Validate req = JAXB.unmarshal(new DOMSource(
				soapElement), Validate.class);
		DwrResponse resp = adapter.validateLogin(req.getArg0(),req.getArg1(),req.getArg2());
		ValidateResponse ret = new ValidateResponse();
		ret.setReturn(resp);
		return ret;
	}

	public void registerService(QName name, IYSNWebService service) {
		this.serviceRegistry.put(name, service);
	}
}